//***************************************************************************//
//*
//* 内容摘要：QOS-NACK处理
//*	
//* 当前版本：V1.0		
//* 作    者：zhfayuan
//* 完成日期：2016-8-21
//**************************************************************************//
#ifndef __CSDQOS_H__
#define __CSDQOS_H__

#include "SDCommDef.h"
#include "SDPacketBuffer.h"
#include "SDRtp.h"

#ifndef NULL
  #ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

#ifndef TRUE
#define TRUE  1
#endif

#ifndef FALSE
#define FALSE 0
#endif

/* Is n belong [a, b] ?  (b-a) >= (n -a) */
#define IS_N_BETWEEN_AB(n, a, b)	(((UINT16)((b) - (a)) >= (UINT16)((n) - (a))))

#define  QOS_QUEUE_NUM		800

//最大允许的丢包等待时间
#define  QOS_DEFAULT_LOSTDELAY	50
#define  QOS_MAX_LOSTDELAY	80
#define  QOS_MIN_LOSTDELAY	0

//QOS丢包等待时间强度，为0时关闭QOS功能实现低延时低抖动，[0, 6]
typedef enum
{
	QOS_REORDER_CAP_DISABLE  = 0,
	QOS_REORDER_CAP_1  = 1,
	QOS_REORDER_CAP_2  = 2,
	QOS_REORDER_CAP_3  = 3,
	QOS_REORDER_CAP_4  = 4,
	QOS_REORDER_CAP_5  = 5,
	QOS_REORDER_CAP_6  = 6,
	QOS_REORDER_CAP_8  = 8
}E_QOS_REORDER_CAPACITY;

//前后序号间隔超过一定范围则认为流重新接入	
#define  QOS_SEQ_VALID_INTERVAL			1000
//NACK模式下，用于重传等待时间估计的RTT最小值（ms）
#define	 QOS_NACK_MIN_RTT				20
//NACK模式下，用于重传等待时间估计的RTT最大值（ms）
#define	 QOS_NACK_MAX_RTT				200
//NACK重传等待时长RTT倍数
#define  QOS_NACK_RTT_LOSTDELAY_RATIO	0.6
//NACK RTT统计的更新速率，瞬时值占的比重
#define  QOS_NACK_RTT_UPDATE_RATIO		0.3

//当NACK请求的包数小于本值时，发端将多次发生重传包以降低重传包再次丢失的概率，默认关闭该功能
#define  QOS_NACK_RETRANS_MORE_THRES	0

//是否降低NACK对码率波动的影响，当开启时，短时间内连续丢包时并非全部发起NACK
//同时限制NACK单次重传的最大包数
#define QOS_NACK_BITRATE_PROTECT_ENABLE	0	

//是否启动NACK收益率监控，收益率过低时将自动关闭NACK
#define QOS_NACK_AUTO_CLOSE_ENABLE		1
//NACK收益率低于本值时触发关闭NACK动作
#define QOS_NACK_AUTO_CLOST_THRESHOLD	0.15

#if QOS_NACK_BITRATE_PROTECT_ENABLE
//NACK单次最多允许重传包数
#define  QOS_NACK_MAX_PACKETS_ONCE		64
#else
//NACK单次最多允许重传包数
#define  QOS_NACK_MAX_PACKETS_ONCE		220
#endif

//默认接收统计信息的统计间隔（毫秒）
#define  QOS_STATIS_INTERVAL_MS			400

typedef enum
{
	QOS_UNUSED		= 0,
	QOS_SEQ_USED	= 1,
} QOS_STATE;

typedef struct T_QOS_DATA_STRUCT
{
	unsigned char*	pBuf;
	int				nLen;
	UINT16	usSeq;
    //unsigned char* 	pBase;
	struct timeval tIn;
} T_QOS_DATA;

typedef struct T_QOSU_STRUCT
{
	int		     m_bRecv;	
	struct timeval       m_tLostDelay;
	//用于统计包间间隔
	long		m_nLastPacketRecvTime;
	UINT		m_unIntervalSum;
	UINT		m_unIntervalCount;
	UINT		m_unIntervalStatics;

	T_QOS_DATA   m_QosData[QOS_QUEUE_NUM];

	UINT16		startPos;
	UINT16		endPos;
	UINT16		startSeq;
	UINT16		endSeq;
	//之前模块输出的的包序号
	UINT16		lastSeq;
} T_QOSU;

//struct timeval 并不是跨平台的，在Linux64上tv_sec、tv_usec均为8字节long，而WIN32 WIN64上为 4字节long
//所以双方交互的struct timeval需要自己定义
typedef struct timevalCommon {
	UINT    tv_sec;         /* seconds */
	UINT    tv_usec;        /* and microseconds */
}timevalCommon;

BOOL TimeIsZeroCommon(timevalCommon *ptimeVal);
//将时间A-时间B结果存入A中，若时间A小于时间B，则返回失败
BOOL TimeMinus(timevalCommon *pA, timevalCommon *pB);

#define QOSU_SUCCESS			0
#define QOSU_INVALID_POINTER	-1
#define QOSU_ERROR_SAMESEQ		-2
#define QOSU_ERROR_LOSTED		-3


class CSDQos
{
public:
	CSDQos();
	virtual ~CSDQos();
	
	int MDStartQos(const char *strName = NULL);
	int MDPutPacketToQos(T_PacketBuffer *pPacketBuffer);
	void MDQosProcess();
	void MDQosDestroy();
	//设置本地接收丢包率（下行丢包率)的统计周期
	void MDrtpSetLostRatioStatisInterval(unsigned short usStatisInterval) { m_usTimeInterval = usStatisInterval; }
	unsigned short MDrtpGetLostRatioStatisInterval() { return m_usTimeInterval; }
	//获得该链路统计周期内的下行丢包率
	float MDrtpGetDownLostRatio();
	//获得该链路统计周期内的下行码率(kbps，包括IP头、UDP头、RTP头)
	float MDrtpGetDownBitrate();
	//获得统计周期内深层次的网络统计数据
	BOOL MDGetTranStatus(int &nLostPkt, int &nPktCount,int &nOutSort);
	//当前总的应收包数、实际收包数(丢包数)
	BOOL MDGetTranTotalStatusBeforeFec(uint64_t &unTotalPacketNumExpect, uint64_t &unTotalPacketNumReal);
	//开关NACK
	void MDQosEnableNack(BOOL bEnable);
	//获取当前NACK使能情况
	BOOL MDGetQosNackEnableStatus() {return m_bEnableNack; }
	//获得当前NACK的成功率
	float MDGetQosNackSuccessRatio();

	//设置QOS丢包等待时间强度，为0时关闭QOS功能实现低延时低抖动
	void MDSetQosReorderCapacity(E_QOS_REORDER_CAPACITY eReorderCap);

private:
	T_QOSU *m_pQosT;

	//用于计算丢包等待时间的乱序恢复强度
	E_QOS_REORDER_CAPACITY m_eReorderCap;

	//用于统计指定时间内的丢包率
	short m_sPrevSeq; 
	short m_sCurrSeq;
	short m_sFirstSeq;
	unsigned short m_usTimeInterval;			//统计的时间周期
	unsigned short m_usPackNumInWindow;			//统计周期内理论应收到的包数
	unsigned short m_usLostPackNumInWindow;     //统计周期内丢失的包数
	unsigned int m_unRecvBytesInWindow;			//统计周期内的接收字节数
	float m_fBitrate;							//统计周期内的码率情况
	float m_fLostRatio;						//反应网络状况的接收丢包比例
	long m_dLastWindowTime;
	unsigned short m_usInWrongSeqInWindow;		//统计周期内乱序到达的包数
	unsigned short m_usInWrongSeqCountInWindow;

	char m_acName[64];

	uint64_t m_unRecvPacketNumTotal;	//生命周期内收到的包数
	uint64_t m_unLostPacketNumTotal;//生命周期内丢失的包数

	uint64_t m_unPrevRecvNumCallGetBitrate; //上一次调用获取下行码率接口时，接收的包数，便于判断是否为0接收状态
	uint64_t m_unPrevRecvNumCallGetLostrate; //同上
	long m_unPrevTimeCallGetBitrate;
	long m_unPrevTimeCallGetLostrate;
	//是否启动NACK功能
	BOOL m_bEnableNack;
#if ENABLE_NACK
	BOOL m_bInNackStatus;	
	unsigned short m_usNackStartSeq;
	unsigned short m_usNackEndSeq;
	//由RTT计算NACK等待时间的控制强度
	float m_fNackWaitRatio;

	//NACK请求情况II的等待时间补充，因为情况II接收的包在NACK发起前已经在本地存放了一定时长
	//若不进行等待时间补充，其NACK请求后的实际等待时长可能不足，导致来不及接收重传媒体包
	UINT m_unNackLostDelayPlusMs;
	//系统到当前总的NACK成功收包数
	UINT m_unNackSuccessPackets;
	//系统当前总的NACK请求包数
	UINT m_unNackReqPackets;
	BOOL m_bDebugFlagForNack;
	//本次NACK发起的时间
	struct timeval m_tNackStartTime;
	//NACK发起到收到第一个期望包的时间统计(NACK RTT)
	UINT m_unNackRttMs;
	//NACK RTT统计的次数
	UINT m_unNackRttCount;
	//NACK RTT统计的最大值
	UINT m_unNackRttMax;
	//是否已收到首个(请求中的任意包)NACK期望包
	BOOL m_bRecvNackPacket;	
#endif

	int MDResetQos(int bDiscard);
	int MDCloseQos(void);
	void MDQosPacketOut(T_QOS_DATA *pData);	
	void MDQosPacketDrop(const T_QOS_DATA *pData);

	virtual void pfQosOutCallback(T_PacketBuffer *pPacketBuffer) = 0;

	//当发现序号不连续时（可能为乱序或者丢包)，并不是所有情况下都需要等待一定时间（最大等待uLostDelay）
	//在FEC配合使用时，若该group的媒体包未丢失，则允许冗余包丢失，此时将不予等待，避免引入不必要的抖动延时。
	virtual BOOL pfNeedWaitForLostPacketCallback(T_PacketBuffer *pPacketBuffer) { return TRUE; }

#if ENABLE_NACK
	//检测当前丢包是否将导致FEC无法恢复，作为NACK发起的依据
	virtual BOOL pfNeedReTransPacketCallback(T_PacketBuffer *pPacketBuffer) { return FALSE; }
	//请求NACK远端重传指定序号的数据包
	virtual void pfNeedRemoteReTransRequestCallback(unsigned short usStartSeq, unsigned short usEndSeq) {}
	//从外部或者内部获取RTT
	UINT MDGetCurrentTransRtt();
	//从外部获取当前RTT时间（外层RTP心跳包获取），用于评估合适的NACK等待时间
	virtual UINT pfGetCurrentTransRttExtern() { return 0;}
	//对端是否使能NACK，只有对端使能的情况下本地NACK才有意义
	virtual BOOL pfRemoteNackIsEnable() { return FALSE; }
#endif
};

struct timeval SDTimeNow();

timevalCommon TimeNowCommon();
#endif
